// IndexedDBWrapper.js

class IndexedDBWrapper {
  constructor(dbName, dbVersion = 1, storeConfigs = []) {
    this.dbName = dbName
    this.dbVersion = dbVersion
    this.storeConfigs = storeConfigs
    this.db = null
    this.openingPromise = null
  }

  // 初始化数据库
  async initDB() {
    if (this.openingPromise) return this.openingPromise

    this.openingPromise = (async () => {
      let db = await this._openDb(this.dbVersion, this.storeConfigs)

      // 检查是否缺少 store
      const missing = this._getMissingStores(db, this.storeConfigs)
      if (missing.length > 0) {
        const newVersion = db.version + 1
        db.close()
        db = await this._openDb(newVersion, missing)
      }

      this.db = db
      this.openingPromise = null
      return db
    })()

    return this.openingPromise
  }

  // 动态新增 store（自动升级）
  async addStores(newStores) {
    const names = new Set(this.storeConfigs.map(s => s.name))
    for (const s of newStores) {
      if (!names.has(s.name)) {
        this.storeConfigs.push(s)
      }
    }

    await this.initDB()

    const missing = this._getMissingStores(this.db, newStores)
    if (missing.length === 0) return this.db

    const newVersion = this.db.version + 1
    this.db.close()
    this.db = await this._openDb(newVersion, missing)
    return this.db
  }

  // 内部获取 store
  _getObjectStore(storeName, mode = "readonly") {
    if (!this.db) throw new Error("Database not initialized. Call initDB() first.")
    const tx = this.db.transaction(storeName, mode)
    return tx.objectStore(storeName)
  }

  // CRUD 方法
  async add(storeName, value) {
    await this.initDB()
    return this._wrap(this._getObjectStore(storeName, "readwrite").add(value))
  }

  async put(storeName, value) {
    await this.initDB()
    return this._wrap(this._getObjectStore(storeName, "readwrite").put(value))
  }
    /**
   * 存在才会更新,不存在则新增
   * @param {*} storeName 
   * @param {*} value 
   * @returns 
   */
async update(storeName, value) {
    await this.initDB()
    return this._wrap(this._getObjectStore(storeName, "readwrite").put(value))
  }
  /**
   * 存在才会更新,不存在则返回false
   * @param {*} storeName 
   * @param {*} value 
   * @returns 
   */
 async updateIfExists(storeName, value) {
    await this.initDB()
    const key = value.id
    const store = this._getObjectStore(storeName, "readwrite")
    return new Promise((resolve, reject) => {
      const getReq = store.get(key)
      getReq.onsuccess = e => {
        if (e.target.result) {
          const putReq = store.put(value)
          putReq.onsuccess = () => resolve(true)   // true 表示更新成功
          putReq.onerror = err => reject(err.target.error)
        } else {
          resolve(false) // false 表示数据不存在，没更新
        }
      }
      getReq.onerror = err => reject(err.target.error)
    })
  }
  async delete(storeName, key) {
    await this.initDB()
    return this._wrap(this._getObjectStore(storeName, "readwrite").delete(key))
  }

  async get(storeName, key) {
    await this.initDB()
    return this._wrap(this._getObjectStore(storeName, "readonly").get(key))
  }

  async getAll(storeName) {
    await this.initDB()
    return this._wrap(this._getObjectStore(storeName, "readonly").getAll())
  }

  async getByIndex(storeName, indexName, key) {
    await this.initDB()
    const store = this._getObjectStore(storeName, "readonly")
    return this._wrap(store.index(indexName).get(key))
  }

  // 遍历游标
  async openCursor(storeName, range = null, direction = "next", handler) {
    await this.initDB()
    return new Promise((resolve, reject) => {
      const request = this._getObjectStore(storeName, "readonly").openCursor(range, direction)
      request.onsuccess = e => {
        const cursor = e.target.result
        if (cursor) {
          try {
            const stop = handler ? handler(cursor) : undefined
            if (stop === true) {
              resolve()
            } else {
              cursor.continue()
            }
          } catch (err) {
            reject(err)
          }
        } else {
          resolve()
        }
      }
      request.onerror = e => reject(e.target.error)
    })
  }

  // 内部：包装 IDBRequest
  _wrap(request) {
    return new Promise((resolve, reject) => {
      request.onsuccess = e => resolve(e.target.result)
      request.onerror = e => reject(e.target.error)
    })
  }

  // 内部：打开数据库并在 onupgradeneeded 中建表
  _openDb(version, storeConfigsToCreate) {
    return new Promise((resolve, reject) => {
      const request = indexedDB.open(this.dbName, version)

      request.onupgradeneeded = ev => {
        const db = ev.target.result
        for (const cfg of storeConfigsToCreate) {
          let store
          if (!db.objectStoreNames.contains(cfg.name)) {
            store = db.createObjectStore(cfg.name, cfg.options || { keyPath: "id" })
          } else {
            store = ev.target.transaction.objectStore(cfg.name)
          }
          if (cfg.indexes) {
            for (const idx of cfg.indexes) {
              if (!store.indexNames.contains(idx.name)) {
                store.createIndex(idx.name, idx.keyPath, idx.options || {})
              }
            }
          }
        }
      }

      request.onsuccess = ev => {
        const db = ev.target.result
        db.onversionchange = () => db.close()
        resolve(db)
      }

      request.onerror = ev => reject(ev.target.error)
      request.onblocked = () => console.warn("IndexedDB open blocked.")
    })
  }

  // 内部：找出缺失的 store
  _getMissingStores(db, storeConfigs) {
    const missing = []
    const set = new Set(Array.from(db.objectStoreNames))
    for (const cfg of storeConfigs) {
      if (!set.has(cfg.name)) missing.push(cfg)
    }
    return missing
  }

  close() {
    this.db && this.db.close()
    this.db = null
  }
}

// --------------------------
// 全局单例工厂
// --------------------------

const dbInstances = new Map()

export function getDB(dbName, version = 1, storeConfigs = []) {
  const key = dbName
  if (!dbInstances.has(key)) {
    dbInstances.set(key, new IndexedDBWrapper(dbName, version, storeConfigs))
  }
  return dbInstances.get(key)
}

//import { getDB } from "./IndexedDBWrapper.js"
// const db = getDB("MyDatabase", 1, [
//   { name: "books", options: { keyPath: "id" }, indexes: [{ name: "byTitle", keyPath: "title" }] },
//   { name: "bookOnLineSetting", options: { keyPath: "id" } }
// ])

// await db.initDB()

// // books 表
// await db.add("books", { id: 1, title: "JavaScript 高级程序设计" })
// const books = await db.getAll("books")
// console.log("books:", books)

// // bookOnLineSetting 表
// await db.add("bookOnLineSetting", { id: "theme", value: "dark" })
// const setting = await db.get("bookOnLineSetting", "theme")
// console.log("setting:", setting)

// // 动态新增 store
// await db.addStores([{ name: "users", options: { keyPath: "id" }, indexes: [{ name: "byEmail", keyPath: "email", options: { unique: true } }] }])
// await db.add("users", { id: "u1", email: "a@b.com", name: "Alice" })